import datetime
import os

import pickle
import sys
from enum import Enum
from typing import List, Dict

from PySide2.QtWidgets import QFileDialog


class SaveType(Enum):
    """保存类型枚举"""
    ExcelAnalysis = 1
    Setting = 2
    Version = 3
    Auth = 4
    ExcelAnalysisDir = 5


class FieldType(Enum):
    """字段类型"""
    Unknow = ""
    Integer = "int"
    Long = "long"
    String = "str"
    Float = "float"
    Double = "double"
    Boolean = "bool"
    intArrComma = "int,List"
    intArrVertiBar = "int|List"
    intArrHashMark = "int#List"
    intArrSemicolon = "int;List"
    floatArrComma = "float,List"
    floatArrVertiBar = "float|List"
    floatArrHashMark = "float#List"
    floatArrSemicolon = "float;List"
    strArrComma = "str,List"
    strArrVertiBar = "str|List"
    strArrHashMark = "str#List"
    strArrSemicolon = "str;List"
    boolArrComma = "bool,List"
    boolArrVertiBar = "bool|List"
    boolArrHashMark = "bool#List"
    boolArrSemicolon = "bool;List"
    strMap = "strMap"
    intMap = "intMap"
    floatMap = "floatMap"
    doubleMap = "doubleMap"
    boolMap = "boolMap"
    str2DMap = "str[,]Map"
    int2DMap = "int[,]Map"
    float2DMap = "float[,]Map"
    double2DMap = "double[,]Map"
    bool2DMap = "bool[,]Map"
    int2DListVertiBar = "int[,]|List"
    float2DListVertiBar = "float[,]|List"
    double2DListVertiBar = "double[,]|List"
    bool2DListVertiBar = "bool[,]|List"
    str2DListVertiBar = "str[,]|List"

    def isArrTypeOrMap(self):
        if (self == FieldType.intArrComma or
                self == FieldType.intArrVertiBar or
                self == FieldType.intArrHashMark or
                self == FieldType.intArrSemicolon or
                self == FieldType.floatArrComma or
                self == FieldType.floatArrVertiBar or
                self == FieldType.floatArrHashMark or
                self == FieldType.floatArrSemicolon or
                self == FieldType.strArrComma or
                self == FieldType.strArrVertiBar or
                self == FieldType.strArrHashMark or
                self == FieldType.strArrSemicolon or
                self == FieldType.boolArrComma or
                self == FieldType.boolArrVertiBar or
                self == FieldType.boolArrHashMark or
                self == FieldType.boolArrSemicolon or
                self == FieldType.strMap or
                self == FieldType.intMap or
                self == FieldType.floatMap or
                self == FieldType.doubleMap or
                self == FieldType.boolMap or
                self == FieldType.str2DMap or
                self == FieldType.int2DMap or
                self == FieldType.float2DMap or
                self == FieldType.double2DMap or
                self == FieldType.bool2DMap or
                self == FieldType.int2DListVertiBar or
                self == FieldType.float2DListVertiBar or
                self == FieldType.double2DListVertiBar or
                self == FieldType.bool2DListVertiBar or
                self == FieldType.str2DListVertiBar):
            return True
        return False

    def isMapType(self):
        if (self == FieldType.strMap or
                self == FieldType.intMap or
                self == FieldType.floatMap or
                self == FieldType.doubleMap or
                self == FieldType.boolMap or
                self == FieldType.str2DMap or
                self == FieldType.int2DMap or
                self == FieldType.float2DMap or
                self == FieldType.double2DMap or
                self == FieldType.bool2DMap):
            return True
        return False

    def isArrayType(self):
        if (self == FieldType.str2DMap or
                self == FieldType.int2DMap or
                self == FieldType.float2DMap or
                self == FieldType.double2DMap or
                self == FieldType.bool2DMap or
                self == FieldType.int2DListVertiBar or
                self == FieldType.float2DListVertiBar or
                self == FieldType.double2DListVertiBar or
                self == FieldType.bool2DListVertiBar or
                self == FieldType.str2DListVertiBar):
            return True
        return False

    def isSplitComma(self):
        if (self == FieldType.intArrComma or
                self == FieldType.floatArrComma or
                self == FieldType.strArrComma or
                self == FieldType.boolArrComma):
            return True
        return False

    def isSplitVertiBar(self):
        if (self == FieldType.intArrVertiBar or
                self == FieldType.floatArrVertiBar or
                self == FieldType.strArrVertiBar or
                self == FieldType.boolArrVertiBar or
                self == FieldType.int2DListVertiBar or
                self == FieldType.float2DListVertiBar or
                self == FieldType.double2DListVertiBar or
                self == FieldType.bool2DListVertiBar or
                self == FieldType.str2DListVertiBar):
            return True
        return False

    def isSplitHashMark(self):
        if (self == FieldType.intArrHashMark or
                self == FieldType.floatArrHashMark or
                self == FieldType.strArrHashMark or
                self == FieldType.boolArrHashMark):
            return True
        return False

    def isSplitSemicolon(self):
        if (self == FieldType.intArrSemicolon or
                self == FieldType.floatArrSemicolon or
                self == FieldType.strArrSemicolon or
                self == FieldType.boolArrSemicolon):
            return True
        return False

    def isArrIntType(self):
        if (self == FieldType.intArrComma or
                self == FieldType.intArrVertiBar or
                self == FieldType.intArrHashMark or
                self == FieldType.intArrSemicolon or
                self == FieldType.int2DListVertiBar or
                self == FieldType.int2DMap or
                self == FieldType.intMap):
            return True
        return False

    def isArrFloatType(self):
        if (self == FieldType.floatArrComma or
                self == FieldType.floatArrVertiBar or
                self == FieldType.floatArrHashMark or
                self == FieldType.floatArrSemicolon or
                self == FieldType.float2DListVertiBar or
                self == FieldType.float2DMap or
                self == FieldType.floatMap):
            return True
        return False

    def isArrStrType(self):
        if (self == FieldType.strArrComma or
                self == FieldType.strArrVertiBar or
                self == FieldType.strArrHashMark or
                self == FieldType.strArrSemicolon or
                self == FieldType.str2DListVertiBar or
                self == FieldType.str2DMap or
                self == FieldType.strMap):
            return True
        return False

    def isArrBoolType(self):
        if (self == FieldType.boolArrComma or
                self == FieldType.boolArrVertiBar or
                self == FieldType.boolArrHashMark or
                self == FieldType.boolArrSemicolon or
                self == FieldType.bool2DListVertiBar or
                self == FieldType.bool2DMap or
                self == FieldType.boolMap):
            return True
        return False

    def isArrDoubleType(self):
        if (self == FieldType.doubleMap or
                self == FieldType.double2DMap):
            return True
        return False

    def is2DListType(self):
        if (self == FieldType.int2DListVertiBar or
                self == FieldType.float2DListVertiBar or
                self == FieldType.double2DListVertiBar or
                self == FieldType.str2DListVertiBar or
                self == FieldType.bool2DListVertiBar):
            return True
        return False

    def is2DMapType(self):
        if (self == FieldType.int2DMap or
                self == FieldType.float2DMap or
                self == FieldType.double2DMap or
                self == FieldType.str2DMap or
                self == FieldType.bool2DMap):
            return True
        return False

    def getFieldType(fType: str):
        if fType == FieldType.Integer.value:
            return FieldType.Integer
        if fType == FieldType.Long.value:
            return FieldType.Long
        if fType == FieldType.String.value:
            return FieldType.String
        if fType == FieldType.Float.value:
            return FieldType.Float
        if fType == FieldType.Double.value:
            return FieldType.Double
        if fType == FieldType.Boolean.value:
            return FieldType.Boolean
        if fType == FieldType.intArrComma.value:
            return FieldType.intArrComma
        if fType == FieldType.intArrVertiBar.value:
            return FieldType.intArrVertiBar
        if fType == FieldType.intArrHashMark.value:
            return FieldType.intArrHashMark
        if fType == FieldType.intArrSemicolon.value:
            return FieldType.intArrSemicolon
        if fType == FieldType.floatArrComma.value:
            return FieldType.floatArrComma
        if fType == FieldType.floatArrVertiBar.value:
            return FieldType.floatArrVertiBar
        if fType == FieldType.floatArrHashMark.value:
            return FieldType.floatArrHashMark
        if fType == FieldType.floatArrSemicolon.value:
            return FieldType.floatArrSemicolon
        if fType == FieldType.strArrComma.value:
            return FieldType.strArrComma
        if fType == FieldType.strArrVertiBar.value:
            return FieldType.strArrVertiBar
        if fType == FieldType.strArrHashMark.value:
            return FieldType.strArrHashMark
        if fType == FieldType.strArrSemicolon.value:
            return FieldType.strArrSemicolon
        if fType == FieldType.boolArrComma.value:
            return FieldType.boolArrComma
        if fType == FieldType.boolArrVertiBar.value:
            return FieldType.boolArrVertiBar
        if fType == FieldType.boolArrHashMark.value:
            return FieldType.boolArrHashMark
        if fType == FieldType.boolArrSemicolon.value:
            return FieldType.boolArrSemicolon
        if fType == FieldType.strMap.value:
            return FieldType.strMap
        if fType == FieldType.intMap.value:
            return FieldType.intMap
        if fType == FieldType.floatMap.value:
            return FieldType.floatMap
        if fType == FieldType.doubleMap.value:
            return FieldType.doubleMap
        if fType == FieldType.boolMap.value:
            return FieldType.boolMap

        if fType == FieldType.str2DMap.value:
            return FieldType.str2DMap
        if fType == FieldType.int2DMap.value:
            return FieldType.int2DMap
        if fType == FieldType.float2DMap.value:
            return FieldType.float2DMap
        if fType == FieldType.double2DMap.value:
            return FieldType.double2DMap
        if fType == FieldType.bool2DMap.value:
            return FieldType.bool2DMap
        if fType == FieldType.int2DListVertiBar.value:
            return FieldType.int2DListVertiBar
        if fType == FieldType.float2DListVertiBar.value:
            return FieldType.float2DListVertiBar
        if fType == FieldType.double2DListVertiBar.value:
            return FieldType.double2DListVertiBar
        if fType == FieldType.bool2DListVertiBar.value:
            return FieldType.bool2DListVertiBar
        if fType == FieldType.str2DListVertiBar.value:
            return FieldType.str2DListVertiBar

        return FieldType.Unknow


class IndexType(Enum):
    """索引方式(生成代码用)"""
    NULL = ""
    """无对应"""
    List = "list"
    """纯列表"""
    Map = "map"
    """字典"""
    Map2List = "map2list"
    """字典索引结果为列表集合"""
    MultKeyMap = "mkMap"
    """多键值字典"""

    def parseFromValue(value: str):
        if value.strip(" ") == IndexType.List.value:
            return IndexType.List
        if value.strip(" ") == IndexType.Map.value:
            return IndexType.Map
        if value.strip(" ") == IndexType.Map2List.value:
            return IndexType.Map2List
        if value.strip(" ") == IndexType.MultKeyMap.value:
            return IndexType.MultKeyMap
        return IndexType.NULL


class Platform(Enum):
    """索引方式(生成代码用)"""
    NULL = ""
    """无对应"""
    Java = "java"
    """Java代码读取"""
    Laya = "laya"
    """Laya代码读取"""
    Unity3d = "u3d"
    """Unity3d代码读取"""

    def parseFromValue(value: str):
        if value.strip(" ") == Platform.Java.value:
            return Platform.Java
        if value.strip(" ") == Platform.Laya.value:
            return Platform.Laya
        if value.strip(" ") == Platform.Unity3d.value:
            return Platform.Unity3d
        return Platform.NULL


class SyncJsonDir:
    """同步本地目录数据"""
    # isOpenFolderChecked: bool = False;
    # """是否被勾选复制后打开（根据勾选同步）"""
    isEnabledChecked: bool = False;
    """是否执行同步"""
    dirPath: str = ""


class SyncPublishServer:
    """是否被勾选复制后打开（根据勾选同步）"""
    isEnabledChecked: bool = False;
    """是否执行同步"""
    serverPath: str = ""
    caption: str = ""
    tag: str = ""
    isSubmitOk: bool = False
    # user: str = ""
    # pwd: str = ""

class FtpPublishServer:
    """是否执行同步"""
    caption: str = ""
    # tag: str = ""
    ip: str = ""
    port: str = ""
    remoteDir: str = ""
    username: str = ""
    password: str = ""
    certPath: str = ""
    isSubmitOk: bool = False
    lastVersion: str = ""

    # user: str = ""
    # pwd: str = ""

class AuthData:
    """账号数据"""
    username: str = None
    password: str = None

    ftpCretPath: str = None
    ftpUsername: str = None
    ftpPassword: str = None

    isClearOldPublishJson:bool = None
    """勾选：发布时是否清理旧配置"""

    def resetDefault(self, force: bool):
        if force or self.username is None:
            self.username = ""
        if force or self.password is None:
            self.password = ""
        if force or self.isClearOldPublishJson is None:
            self.isClearOldPublishJson = False
        if force or self.ftpCretPath is None:
            self.ftpCretPath = ""
        if force or self.ftpUsername is None:
            self.ftpUsername = ""
        if force or self.ftpPassword is None:
            self.ftpPassword = ""

class SettingData:
    """配置页数据保存"""

    syncDirs: dict[str, SyncJsonDir] = None
    """同步本地目录数据"""

    syncServers: dict[str, SyncPublishServer] = None
    """同步服务器配置数据"""

    ftpServers: dict[str, FtpPublishServer] = None
    """同步服务器配置数据"""

    jsonLastReleasePath: str = ""
    """svn生成最后版本路径"""

    # 常量数值
    groupPrefix: str = "group_"
    """组常量标识字头"""
    libTablePrefix: str = "TB_"
    """单标常量标识字头"""
    libEventPrefix: str = "EVENT_LIB_LOAD_"
    """单标常量标识字头"""
    svnDirJava: str = "/JavaVoCode"
    """svn中Java代码保存目录"""
    svnDirLaya: str = "/LayaVoCode"
    """svn中Laya代码保存目录"""
    svnDirJson: str = "/JsonData"
    """svn中Json数据保存目录"""

    svnTempFolder: str = "temp\\"

    svnNewestFolder: str = "last\\"
    svnNewestSvnUrl: str = "last/"
    svnHistoryFolder: str = "history\\"
    svnHistorySvnUrl: str = "history/"
    svnRemoteHistoryFolder: str = "remote_history\\"
    svnRemoteHistorySvnUrl: str = "history/"

    # 可配置数值

    readerCfgSheetName: str = None
    """配置页Sheet名"""
    readerCfgTagImportSheets: str = None
    """TAG-导入表名"""
    readerCfgIgnoreColumn: str = None
    """不导出标识"""

    defDirExcelFile: str = None
    """Excel默认目录"""
    defDirJsonFile: str = None
    """导出Json目录"""
    defDefineJsonFile: str = None
    """主定义Json文件名"""
    genDirJavaFile: str = None
    """Java代码目录"""
    genDirLayaFile: str = None
    """Laya代码目录"""
    genDirJavaFileCopy: str = None
    """Java代码拷贝目录"""
    genDirLayaFileCopy: str = None
    """Laya代码拷贝目录"""
    genZipFile: str = None
    """生成ZIP的文件名"""

    chkUpdateCopy: bool = False
    """是否更新后拷贝指指定目录"""
    chkGenSvn: bool = False
    """是否提交SVN"""
    chkDelOld: bool = False
    """是否从SVN删除老旧没用类"""
    chkGenCopy: bool = False
    """是否生成后拷贝指指定目录"""

    chkAutoOpenFolderGenCode: bool = False
    """代码生成后自动打开目录"""

    chkAutoCopyGenJson: bool = False
    """JSON生成后自动把代码拷贝到指定项目目录"""
    chkAutoOpenFolderGenJson: bool = False
    """生成Json数据后自动打开目录"""
    chkAutoOpenFolderCopy: bool = False
    """同步拷贝数据后自动打开目录"""

    chkClearDirGen: bool = False
    """SVN拉取或生成前清理目录旧临时文件"""
    chkClearDirCopy: bool = False
    """目标拷贝目录清理所有文件"""
    chkZip: bool = False
    """是否打包成ZIP压缩包"""

    genLibConstantClass: str = None
    """Lib生成常量表类名"""
    genLibEasyAccessClass: str = None
    """Lib简易读取类类名"""
    genLibPrefix: str = None
    """Lib实体类字头"""

    svnUrl: str = None
    """数据存放SVN地址"""
    svnUser: str = None
    """数据存放SVN账号"""
    svnPwd: str = None
    """数据存放SVN密码"""

    cfgServerUrl: str = None
    """配置服务器ip地址"""
    cfgServerUser: str = None
    """配置服务器鉴权账号"""
    cfgServerPwd: str = None
    """配置服务器鉴权密码"""

    defServerAddr: str = None
    """发布时服务器地址"""

    def swapSyncDirsItems(self, index1, index2):
        """将datas两个换位"""
        # 将字典的项转换为键值对列表
        items = list(self.syncDirs.items())
        # 交换指定索引的键值对
        items[index1], items[index2] = items[index2], items[index1]
        # 将交换后的列表转换回字典
        self.syncDirs = dict(items)

    def swapSyncServersItems(self, index1, index2):
        """将datas两个换位"""
        # 将字典的项转换为键值对列表
        items = list(self.syncServers.items())
        # 交换指定索引的键值对
        items[index1], items[index2] = items[index2], items[index1]
        # 将交换后的列表转换回字典
        self.syncServers = dict(items)

    def resetDefault(self, force: bool):
        if force or self.syncDirs is None:
            self.syncDirs = dict()
        if force or self.syncServers is None:
            self.syncServers = dict()
        if force or self.ftpServers is None:
            self.ftpServers = dict()

        if force or self.readerCfgSheetName is None:
            self.readerCfgSheetName = "CFG"
        if force or self.readerCfgTagImportSheets is None:
            self.readerCfgTagImportSheets = "导入表名"
        if force or self.readerCfgIgnoreColumn is None:
            self.readerCfgIgnoreColumn = "不导,不导出,跳过,忽略,skip,ignore"
        if force or self.defDirExcelFile is None:
            self.defDirExcelFile = "excel\\"
        if force or self.defDirJsonFile is None:
            self.defDirJsonFile = "output\\"
        if force or self.defDefineJsonFile is None:
            self.defDefineJsonFile = "_definition.json"
        if force or self.genDirJavaFile is None:
            self.genDirJavaFile = "java\\"
        if force or self.genDirLayaFile is None:
            self.genDirLayaFile = "laya\\"
        if force or self.genDirJavaFileCopy is None:
            self.genDirJavaFile = "javaSourePath\\"
        if force or self.genDirLayaFileCopy is None:
            self.genDirLayaFile = "layaSourePath\\"
        if force or self.genZipFile is None:
            self.genZipFile = "lib.zip"

        if force or self.genLibConstantClass is None:
            self.genLibConstantClass = "ExcelLibConstant"
        if force or self.genLibEasyAccessClass is None:
            self.genLibEasyAccessClass = "ExcelLibDataEasyAccess"
        if force or self.genLibPrefix is None:
            self.genLibPrefix = "Lib"

        if self.svnUrl is None:
            self.svnUrl = "svn://"
        if self.svnUser is None:
            self.svnUser = ""
        if self.svnPwd is None:
            self.svnPwd = ""

        if self.cfgServerUrl is None:
            self.cfgServerUrl = "http://127.0.0.1:8080/"
        if self.cfgServerUser is None:
            self.cfgServerUser = "user"
        if self.cfgServerPwd is None:
            self.cfgServerPwd = "10242048"

        if self.defServerAddr is None:
            self.defServerAddr = ""


class Union:
    """关联配置"""
    myKey: str
    """本表键名"""
    unionTableName: str
    """关联表的Sheet名"""
    unionKey: str
    """关联表对应的键名(类型必须与myKey所指字段一致)"""

    def parseFromValue(value: str):
        arr = value.split("=")
        if len(arr) != 2:
            return None
        myKey = arr[0].strip(" ")
        if len(myKey) < 1:
            return None
        arr2 = arr[1].split(".")
        if len(arr2) != 2:
            return None
        table = arr2[0].strip(" ")
        key = arr2[1].strip(" ")
        if len(table) < 1 or len(key) < 1:
            return None
        union: Union = Union()
        union.myKey = myKey
        union.unionTableName = table
        union.unionKey = key
        return union


class FieldData:
    """字段数据"""
    colunmOrd: int = -1
    """在表格中第几列"""
    fieldName: str = ""
    """表格中的描述字段名"""
    fieldType: FieldType = FieldType.Unknow
    """字段类型，对应FieldType """
    fieldKey: str = ""
    """字段导出类中键名"""
    fieldRestrain: str = ""
    """字段约束"""
    canNotEmpty: bool = False
    """字段不能为空"""
    isMainKey: bool = False
    """本字段是否主键"""


class LoadConfig:
    """代码加载配置"""
    type: IndexType
    """索引类型"""
    keyFields: List[FieldData]
    keyFieldStrings: List[str]
    """多键值字段键名"""
    exportPlatforms: Dict[str, Platform] = dict()
    """导出平台列表。枚举:Platform[] \n检测是否存在使用Platform.Java in platforms.values()"""
    unions: List[Union] = []
    """关联配置。类：Union[]"""
    loadWeight: int = 0
    """加载先后权重"""
    group: str = ""
    """加载组别"""
    loadRelations: List[str] = []
    """关联加载(表json名)"""
    customDataFullName: str = ""

    def getDataFullName(self, table):
        """代码中识别用的名字, 带索引类型名，带索引字段名"""
        key: str = ""
        for kf in self.keyFields:
            ss = list(kf.fieldKey)
            ss[0] = ss[0].upper()
            key += "".join(ss)
        if self.type is IndexType.Map:
            return table.jsonName + "MapBy" + key
        elif self.type is IndexType.List:
            return table.jsonName + "List"
        elif self.type is IndexType.MultKeyMap:
            return table.jsonName + "MkMapBy" + key
        elif self.type is IndexType.Map2List:
            return table.jsonName + "MapListBy" + key

    def getDataName(self, table):
        if self.customDataFullName != "":
            return table.jsonName + self.customDataFullName
        else:
            """代码中识别用的名字, 带索引类型名"""
            if self.type is IndexType.Map:
                return table.jsonName + "Map"
            elif self.type is IndexType.List:
                return table.jsonName + "List"
            elif self.type is IndexType.MultKeyMap:
                return table.jsonName + "MkMap"
            elif self.type is IndexType.Map2List:
                return table.jsonName + "MapList"

    def getConstantDataFullName(self, table):
        """代码中识别用的名字, 带索引类型名，带索引字段名"""
        key: str = ""
        for kf in self.keyFields:
            key += kf.fieldKey + "_"
        if self.type is IndexType.Map:
            return table.jsonName.upper() + "_MAP_" + key.upper()
        elif self.type is IndexType.List:
            return table.jsonName.upper() + "_LIST_"
        elif self.type is IndexType.MultKeyMap:
            return table.jsonName.upper() + "_MKMAP_" + key.upper()
        elif self.type is IndexType.Map2List:
            return table.jsonName.upper() + "_MAPLIST_" + key.upper()


class TableData:
    """Excel表格数据"""
    fields: List[FieldData] = []
    """字段数据"""
    sheetName: str = ""
    """Sheet标签名"""
    jsonName: str = ""
    """导出json文件名(不含.json)"""
    loadConfigs: List[LoadConfig] = []
    """索引配置，LoadConfig[]"""
    fieldNameRowIndex: int = -1;
    """字段中文名在第几行"""
    fieldKeyRowIndex: int = -1;
    """字段名在第几行"""
    fieldTypeRowIndex: int = -1;
    """字段类型在第几行"""
    fieldRestrainRowIndex: int = -1;
    """字段约束在第几行"""
    fieldDataRowIndex: int = -1;
    """正式数据起始在第几行"""
    isListChecked: bool = False;
    """是否被勾选（根据勾选同步）"""
    lastExecuteTime: datetime = None
    """最后一次生成时间"""
    lastMd5: str = ""
    """最后一次生成的Md5"""
    lastVersion: str = ""
    """最后一次生成的版本"""
    exportLibPlatforms: Dict[str, Platform]
    """导出平台列表。枚举:Platform[] \n检测是否存在使用Platform.Java in platforms.values()"""

    def __init__(self):
        self.exportLibPlatforms = dict()
        self.loadConfigs = []

    def syncExportPlatformsFromConfigs(self):
        """从config获知本表导出什么平台用"""
        # self.exportLibPlatforms: Dict[str, Platform] = dict()
        for config in self.loadConfigs:
            for key, val in config.exportPlatforms.items():
                if val not in self.exportLibPlatforms.values():
                    self.exportLibPlatforms[key] = val

    def getClassname(self):
        """获取生成代码的类名"""
        edata: EData = EData()
        className: str = self.jsonName
        classNameUpper = list(className)
        classNameUpper[0] = classNameUpper[0].upper()
        className = edata.setting.genLibPrefix + "".join(classNameUpper)
        return className


class ExcelData:
    """Excel文件数据"""
    tables: Dict[str, TableData]
    """Sheet表，key使用导出json名"""
    fileName: str = ""
    """导入文件名"""
    filePath: str = ""
    """导入时的文件全路径"""
    lastExecuteTime: datetime = None
    """最后一次生成时间"""

    def __init__(self):
        tables = dict()


class Version:
    isListChecked: bool = False;
    """是否被勾选（根据勾选同步）"""
    version: str = ""
    buildDate: str = ""
    dirPath: str = ""


class VersionData:
    """版本数据"""
    versions: List[Version]
    lastUploadVersion: Version = None

    def __init__(self):
        versions = []


class JsonListVersionData:
    """发布界面Json列表用的文件版本数据"""
    tag: str = ""
    version: str = ""
    dateStr: str = ""
    date: datetime = None
    path: str = ""
    author: str = ""
    count: int = -1
    md5: str = ""
    isUsable: bool = True
    isDuplicate: bool = False


class ExcelDataFile():
    datas: Dict[str, ExcelData]
    """单个excel环境的多个excel数据"""
    tag: str
    """按钮标记"""
    fileDir: str
    """导入文件时目录"""

    def __init__(self, tagName):
        self.datas = dict()
        self.tag = tagName
        self.fileDir = ""

    def clear(self):
        self.fileDir = ""
        self.datas.clear()

    def swapDataItems(self, index1, index2):
        """将datas两个换位"""
        # 将字典的项转换为键值对列表
        items = list(self.datas.items())
        # 交换指定索引的键值对
        items[index1], items[index2] = items[index2], items[index1]
        # 将交换后的列表转换回字典
        self.datas = dict(items)


class ExcelDataFiles():
    currUseExcelDataOrd = 0
    """记录顺序"""

    # currUseExcelDataCount = 1
    """记录顺序"""

    dataFiles: List[ExcelDataFile]
    """多个不同目录切换用的记录"""

    # buttonDefNames = ["[环境1]>", "[环境2]", "[环境3]", "[环境4]", "[环境5]", "[环境6]", "[环境7]", "[环境8]",
    #                   "[环境9]", "[环境10]"];
    # """默认按钮名称"""

    maxDirCount = 20
    """最多多少个环境目录"""

    def __init__(self):
        self.currUseExcelDataOrd = 0
        self.dataFiles = []
        self.addOneData()
        # self.tryFillData()

    # def tryFillData(self):
    #     for i in range(0, self.currUseExcelDataCount):
    #         if (len(self.dataFiles) <= i):
    #             self.dataFiles.append(ExcelDataFile("[环境" + str(i + 1) + "]"))

    def addOneData(self):
        if self.dataCount < self.maxDirCount:
            dataFile: ExcelDataFile = ExcelDataFile("[环境" + str(self.dataCount + 1) + "]")
            self.dataFiles.append(dataFile)
            return dataFile, self.dataCount - 1
        return None

    def removeOneData(self, ord):
        if self.dataCount <= 1:
            return None
        if ord < self.dataCount:
            dataFile = self.dataFiles.pop(ord)
            return dataFile

    @property
    def dataCount(self):
        """当前Excel空间数量"""
        return len(self.dataFiles)

    @property
    def isDataLimitReached(self):
        """Excel空间是否已经到达上限"""
        return len(self.dataFiles) >= self.maxDirCount

    @property
    def selectedDatas(self):
        datafile = self.dataFiles[self.currUseExcelDataOrd]
        return datafile.datas

    @property
    def selectedExcel(self):
        return self.dataFiles[self.currUseExcelDataOrd]

    @property
    def files(self):
        return self.dataFiles


class EData():
    """数据持久化(单例)"""

    DEFAULT_SAVE_DATA_EXCEL_DATA_NAME = "Excel2JsonData"
    DEFAULT_SAVE_DATA_EXCEL_DATA_EXT = ".data"
    DEFAULT_SAVE_DATA_EXCEL_ANALYSIS = "Excel2JsonData.data"
    DEFAULT_SAVE_DATA_EXCEL_SETTING = "Excel2JsonSetting.data"
    DEFAULT_SAVE_DATA_EXCEL_VERSION = "Excel2JsonPublishVersion.data"
    DEFAULT_SAVE_DATA_EXCEL_AUTH = "ExcelAuthor.data"
    # DEFAULT_SAVE_TABLE_FILE = "Excel2JsonTable.data"

    excelDatas: ExcelDataFiles
    """excel解析保存"""
    setting: SettingData
    """settingData"""
    version: VersionData
    """版本发布数据"""
    authData: AuthData
    """发布账号密码数据"""

    # 不打包获取当前目录
    currentPath: str = ""

    # 打包获取当前目录
    # self.current_path = os.path.dirname(os.path.realpath(sys.executable))

    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            cls._instance = super().__new__(cls, *args, **kwargs)
            print("第一次创建EData")
            # 测试过最好的获取执行目录方式
            cls.currentPath = os.path.dirname(os.path.realpath(sys.argv[0]))
            if not cls.currentPath.endswith("\\"):
                cls.currentPath += "\\"

            # 不打包获取当前目录
            # cls.currentPath = os.path.dirname(os.path.abspath(__file__))
            # 打包获取当前目录
            # cls.currentPath = os.path.dirname(os.path.realpath(sys.executable))
            cls.excelDatas = ExcelDataFiles()
            cls.setting = SettingData()
            cls.version = VersionData()
            cls.authData = AuthData()
        return cls._instance

    def restoreFromFile(self):
        self.load(SaveType.ExcelAnalysis, SaveType.Setting, SaveType.Version,
                  SaveType.Auth)  # 打开程序时尝试恢复之前软件状态，读取Excel2JsonData.data文件
        self.setting.resetDefault(False)  # 每次打开时检测配置数据有没有None的，有none则补充为默认值
        self.authData.resetDefault(False)

    def save(self, *saveTypes):
        """保存当前数据
           save(SaveType1,SaveType2..) -> bool
        """
        for type in saveTypes:
            print("SAVE:" + type.name)
            if type is SaveType.ExcelAnalysis:
                f = open(self.DEFAULT_SAVE_DATA_EXCEL_ANALYSIS, "wb")
                pickle.dump(self.excelDatas, f)
                f.close()
            if type is SaveType.Setting:
                f = open(self.DEFAULT_SAVE_DATA_EXCEL_SETTING, "wb")
                pickle.dump(self.setting, f)
                f.close()
            if type is SaveType.Version:
                f = open(self.DEFAULT_SAVE_DATA_EXCEL_VERSION, "wb")
                pickle.dump(self.version, f)
                f.close()
            if type is SaveType.Auth:
                f = open(self.DEFAULT_SAVE_DATA_EXCEL_AUTH, "wb")
                pickle.dump(self.authData, f)
                f.close()
        return True

    def exportPublishServerList(self):
        pass

    def importPublishServerList(self):
        pass

    def load(self, *saveTypes):
        """读取数据
           load(SaveType1,SaveType2..) -> bool
        """
        for type in saveTypes:
            print("LOAD:" + type.name)
            if type is SaveType.ExcelAnalysis:
                if not os.path.isfile(self.DEFAULT_SAVE_DATA_EXCEL_ANALYSIS):
                    data = ExcelDataFiles()
                    self.excelDatas = data
                    continue
                f = open(self.DEFAULT_SAVE_DATA_EXCEL_ANALYSIS, "rb")
                self.excelDatas = pickle.load(f)
                f.close()
            if type is SaveType.Setting:
                if not os.path.isfile(self.DEFAULT_SAVE_DATA_EXCEL_SETTING):
                    continue
                f = open(self.DEFAULT_SAVE_DATA_EXCEL_SETTING, "rb")
                self.setting = pickle.load(f)
                f.close()
            if type is SaveType.Version:
                if not os.path.isfile(self.DEFAULT_SAVE_DATA_EXCEL_VERSION):
                    continue
                f = open(self.DEFAULT_SAVE_DATA_EXCEL_VERSION, "rb")
                self.version = pickle.load(f)
                f.close()
            if type is SaveType.Auth:
                if not os.path.isfile(self.DEFAULT_SAVE_DATA_EXCEL_AUTH):
                    continue
                f = open(self.DEFAULT_SAVE_DATA_EXCEL_AUTH, "rb")
                self.authData = pickle.load(f)
                f.close()
        return True
